package com.cskaoyan.javase._2inner._6lambda._4example;

/**
 * 方法需要功能接口对象,往往是需要功能接口实现的一个方法
 * 这个方法传入另外一个方法后,往往表示一种规则,比如:
 *      1.计算的规则
 *      2.过滤规则
 *      3.映射规则
 *      4.排序规则
 *      5.规约规则
 *      ...
 *
 * 这里我们演示一下,使用"方法的回调"实现过滤规则,增加方法调用的灵活性
 * 这里我们过滤数组容器,要求能够过滤所有的数组,实现所有的过滤规则
 *
 * 分析:
 * 过滤的规则,实际上是一个方法
 * 我们需要将过滤的规则,传入一个过滤的方法,需要使用方法回调
 * 过滤的规则我们可以写一个功能接口,来放过滤规则的抽象方法
 *
 * @since 09:55
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        // 测试
        Student[] studs = new Student[5];
        studs[0] = new Student(18, 10);
        studs[1] = new Student(17, 90);
        studs[2] = new Student(20, 80);
        studs[3] = new Student(30, 50);
        studs[4] = new Student(10, 59);
        // 过滤学生数组,规则一: 不及格的童鞋不要,及格的童鞋留下
        Object[] result = filterArrByIFilter(studs, o -> ((Student) o).score >= 60);
        /*
            对象数组是不能做强转的
         */
        for (Object o : result) {
            ((Student) o).print();
        }

        System.out.println("-----------------------------");
        // 过滤学生数组,规则二: 成年的学生留下,未成年的不要
        Object[] result2 = filterArrByIFilter(studs, o -> ((Student) o).age >= 18);
        for (Object o : result2) {
            ((Student) o).print();
        }

        String[] strs = {"abc", "abc123", "abc", "123"};
        System.out.println("----------------------------------------");
        // 过滤字符串数组,规则一: 字符串"abc"不要,不是"abc"就留下
        Object[] results3 = filterArrByIFilter(strs, o -> (!((String) o).equals("abc")));
        for (Object o : results3) {
            System.out.println(((String) o));
        }

        // 基本数据类型数组,需要使用包装类型数组
        Integer[] its = {1, 2, 3, 4, 5};
        Object[] result4 = filterArrByIFilter(its, o -> ((Integer) o) <= 1);
        System.out.println("-----------------------");
        for (Object o : result4) {
            System.out.println(((int) o));
        }


    }

    /*
        过滤的规则,过滤的方法,最终需要传入一个过滤的方法
        这个过滤的方法表示过滤数组,说白了过滤数组中的元素,相当于删除数组元素(真的删除)
        这个过程不能直接在原数组中完成,需要创建新数组
        所以方法需要返回这个新数组

        方法的形参列表如何写呢?
        首先肯定需要传入一个老数组,对老数组做过滤
        除此之外,还需要传入功能接口的实现,表示过滤的规则
     */
    public static Object[] filterArrByIFilter(Object[] old, IFilter filter) {
        /*
            过滤数组元素:
                1.计数创建新数组,计算出留下元素的个数,留下元素的个数就是新数组的长度
                2.给新数组赋值,遍历老数组,只要是过滤掉的元素,直接跳过循环,不赋值
                3.返回新数组
         */
        int count = 0;
        for (Object o : old) {
            if (filter.accept(o)) {
                // 表示留下的元素
                count++;
            }
        }
        // count就表示留下元素的个数,就是新数组的长度
        // 创建新数组
        Object[] result = new Object[count];
        // 给新数组赋值
        int newIndex = 0;
        for (Object o : old) {
            // 被过滤掉的元素,直接跳过循环
            if (!filter.accept(o)) {
                // 表示过滤掉的元素
                continue;
            }
            // 留下的元素,给新数组赋值
            result[newIndex] = o;
            newIndex++;
        }
        // 返回新数组
        return result;
    }
}

// 过滤规则的功能接口
interface IFilter {
    /*
        过滤规则的抽象方法,如何写呢?
        这个过滤规则表示对容器(数组)中元素的过滤
        所以数组中的所有元素,都要经过该方法的筛选
        (数组中的所有元素,都要传入这个方法,然后结果是留下或者过滤掉)
        方法结果具有二元性,返回值类型写布尔类型
        为了能够过滤所有数组
        所以形参类型写Object
     */

    /**
     * true表示留下,false表示过滤掉
     * @param o 被过滤的数组的中元素
     * @return boolean
     * @author wuguidong@cskaoyan.onaliyun.com
     * @since 2022/04/25 10:08
     */
    boolean accept(Object o);
}

class Student {
    int age;
    double score;

    public Student() {
    }

    public Student(int age, double score) {
        this.age = age;
        this.score = score;
    }

    public void print() {
        System.out.println("年龄是" + age + ", 成绩是" + score);
    }
}